#ifdef __GNUG__
#pragma implementation
#endif

#include "SunColorMap.h"

#include "Math.h"
#include "Env.h"
#include "String.h"
#include "WindowSystem.h"
#include "SunWindowPort.h"

#include "pix.h"

//---- SunColorMap -------------------------------------------------------------

int SunColorMap::cmsno= 1;
byte *SunColorMap::gamma_map;

SunColorMap::SunColorMap(int d)
{
	int i;

	if (d == 8) {
		length= 254;
		rw= TRUE;
		if (gGreyScale)
			name= strsave("et_cms");
		else
			name= strsave("et_grey");
		grey= gGreyScale;
	} else if (d == 1) {
		length= 2;
		rw= FALSE;
		name= strsave("monochrome");
		grey= TRUE;
	}

	changed= TRUE;

	usecnt= new int[length];
	type= new char[length];

	realmap[0].red= realmap[0].green= realmap[0].blue= 255;         /* white */
	usecnt[0]= 5000000;
	realmap[1].red= realmap[1].green= realmap[1].blue= 0;  /* black */
	usecnt[1]= 5000000;

	if (grey) {
		for (i= 2; i < length-2; i++) {
			realmap[i].red= 255-i;
			realmap[i].green= 255-i;
			realmap[i].blue= 255-i;
			usecnt[i]= 1000000;
		}
	} else {
		if (length == 254) {
			int r, g, b, j;

			i= 2;
		
			for (r= 0; r < MAXREDS; r++) {
				for (g= 0; g < MAXGREENS; g++) {
					for (b= 0; b < MAXBLUES; b++) {
						//if (r == g && g == b)
						//    continue;
						int r0= (r*255)/(MAXREDS-1),
							g0= (g*255)/(MAXGREENS-1),
							b0= (b*255)/(MAXBLUES-1);

						realmap[i].red= r0;
						realmap[i].green= g0;
						realmap[i].blue= b0;
						usecnt[i]= 1000000;
						
						rgbmap[r*MAXGREENS*MAXBLUES + g*MAXBLUES + b]= i;

						i++;
					}
				}
			}
			for (j= 0; j < MAXGREYS; j++) {
				realmap[i].red= realmap[i].green= realmap[i].blue= (j*255)/(MAXGREYS-1);
				usecnt[i]= 2000000;
				i++;
			}
		}
	}
	realmap[length-2].red= realmap[length-2].green= realmap[length-2].blue= 255;  /* white */
	usecnt[length-2]= 5000000;
	realmap[length-1].red= realmap[length-1].green= realmap[length-1].blue= 0;  /* black */
	usecnt[length-1]= 5000000;

	for (i= 0; i < 256; i++)
		graymap[i]= FindGray(i);
}

SunColorMap::SunColorMap(SunColorMap *from) : WindowIndexMapper(from)
{
	name= strsave(form("et_cms-%d", cmsno++));

	changed= TRUE;

	usecnt= new int[length];
	type= new char[length];

	MemCpy(usecnt, from->usecnt, length * sizeof(int));
	MemCpy(type, from->type, length * sizeof(char));
}

WindowColorMap *SunColorMap::DevMakeCopy(WindowColorMap *from)
{
	return new SunColorMap((SunColorMap*)from);
}

SunColorMap::~SunColorMap()
{
	SafeDelete(name);
	SafeDelete(usecnt);
	SafeDelete(type);
}

void SunColorMap::DevInstall(Port *port)
{
	if (length == 2)
		return;
	pixwin *pw= ((SunWindowPort*)port)->pw;
	register int i;

	if (gamma_map == 0) {
		gamma_map= new byte[length];
		float ga= 1000.0 / (float) Env::GetValue("WindowSystem.Gamma", 1600);
		for (i= 0; i < 256; i++)
			gamma_map[i]= (u_char) (Math::Power(((float)i / 255.0), ga) * 255);
	}

	u_char r[256], g[256], b[256];
	for (i= 0; i < length; i++) {
		r[i]= gamma_map[realmap[i].red];
		g[i]= gamma_map[realmap[i].green];
		b[i]= gamma_map[realmap[i].blue];
	}

	pw->pw_clipdata->pwcd_flags |= PWCD_SET_CMAP_SIZE;
	pw_setcmsname(pw, name);
	pw_putcolormap(pw, 0, length, r, g, b);

	changed= FALSE;
}

u_long SunColorMap::FindRGB(register RGB *c)
{
	register RGB *mp;
	register int rd, gd, bd, i, dist, minix= 0, mindist= 3*256*256;

	for (i= 0, mp= realmap; i < length; i++, mp++) {
		if (type[i])
			continue;
		rd= mp->red  - c->red;
		gd= mp->green- c->green;
		bd= mp->blue - c->blue;
		dist= rd*rd + gd*gd + bd*bd;
		
		if (dist < mindist) {
			mindist= dist;
			minix= i;
		}
	}
	return minix;
}

u_long SunColorMap::FindGray(int gray)
{
	register RGB *mp;
	register int i, minix= 0, dist, mindist= 256;

	for (i= 0, mp= realmap; i < length; i++, mp++) {
		if (type[i] || !(mp->red == mp->green && mp->green == mp->blue))
			continue;
		dist= mp->red - gray;
		if (dist < 0)
			dist= -dist;
		if (dist < mindist) {
			mindist= dist;
			minix= i;
		}
	}
	return minix;
}

u_long SunColorMap::DevRGB2Index2(RGB*)
{
	u_long ix= 0;

	return ix;
}

u_long SunColorMap::DevAllocateAndSetCell(long id, RGB *c, WindowPort*)
{
	register int i, minuse= 0x7ffffff0;

	if (id < 0) {   // allocate new r/w cell
		for (i= 0; i < length; i++) {
			if (type[i])
				continue;
			if (usecnt[i] < minuse) {
				minuse= usecnt[i];
				id= i;
			}
		}
		type[id]= 1;
		usecnt[id]++;
	}
	if (grey) {
		int v= c->AsGreyLevel();
		if (realmap[id].red != v) {
			realmap[id].red= realmap[id].green= realmap[id].blue= v;
			changed= TRUE;
		}
	} else {
		if (realmap[id].red != c->red || realmap[id].green != c->green || realmap[id].blue != c->blue) {
			realmap[id]= *c;
			changed= TRUE;
		}
	}
	return id;
}
